#!/usr/bin/env python
# -*- coding: utf-8 -*-

# refer to `https://bitbucket.org/akorn/wheezy.captcha`

import random
import string
import os.path
from io import BytesIO

from PIL import Image
from PIL import ImageFilter
from PIL.ImageDraw import Draw
from PIL.ImageFont import truetype


class Bezier:
    def __init__(self):
        self.tsequence = tuple([t / 20.0 for t in range(21)])
        self.beziers = {}

    def pascal_row(self, n):
        """ Returns n-th row of Pascal's triangle
        """
        result = [1]
        x, numerator = 1, n
        for denominator in range(1, n // 2 + 1):
            x *= numerator
            x /= denominator
            result.append(x)
            numerator -= 1
        if n & 1 == 0:
            result.extend(reversed(result[:-1]))
        else:
            result.extend(reversed(result))
        return result

    def make_bezier(self, n):
        """ Bezier curves:
            http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
        """
        try:
            return self.beziers[n]
        except KeyError:
            combinations = self.pascal_row(n - 1)
            result = []
            for t in self.tsequence:
                tpowers = (t ** i for i in range(n))
                upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
                coefs = [c * a * b for c, a, b in zip(combinations,
                                                      tpowers, upowers)]
                result.append(coefs)
            self.beziers[n] = result
            return result


class Captcha(object):
    def __init__(self):
        self._bezier = Bezier()
        self._dir = os.path.dirname(__file__)
        # self._captcha_path = os.path.join(self._dir, '..', 'static', 'captcha')

    @staticmethod
    def instance():
        if not hasattr(Captcha, "_instance"):
            Captcha._instance = Captcha()
        return Captcha._instance

    def initialize(self, width=200, height=75, color=None, text=None, fonts=None):
        # self.image = Image.new('RGB', (width, height), (255, 255, 255))
        self._text = text if text else random.sample(string.ascii_uppercase + string.ascii_uppercase + '3456789', 4)
        self.fonts = fonts if fonts else \
            [os.path.join(self._dir, 'fonts', font) for font in ['Arial.ttf', 'Georgia.ttf', 'actionj.ttf']]
        self.width = width
        self.height = height
        self._color = color if color else self.random_color(0, 200, random.randint(220, 255))

    @staticmethod
    def random_color(start, end, opacity=None):
        red = random.randint(start, end)
        green = random.randint(start, end)
        blue = random.randint(start, end)
        if opacity is None:
            return red, green, blue
        return red, green, blue, opacity

    # draw image

    def background(self, image):
        Draw(image).rectangle([(0, 0), image.size], fill=self.random_color(238, 255))
        return image

    @staticmethod
    def smooth(image):
        return image.filter(ImageFilter.SMOOTH)

    def curve(self, image, width=4, number=6, color=None):
        dx, height = image.size
        dx /= number
        path = [(dx * i, random.randint(0, height))
                for i in range(1, number)]
        bcoefs = self._bezier.make_bezier(number - 1)
        points = []
        for coefs in bcoefs:
            points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
                                for ps in zip(*path)))
        Draw(image).line(points, fill=color if color else self._color, width=width)
        return image

    def noise(self, image, number=50, level=2, color=None):
        width, height = image.size
        dx = width / 10
        width -= dx
        dy = height / 10
        height -= dy
        draw = Draw(image)
        for i in range(number):
            x = int(random.uniform(dx, width))
            y = int(random.uniform(dy, height))
            draw.line(((x, y), (x + level, y)), fill=color if color else self._color, width=level)
        return image

    def text(self, image, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
        color = color if color else self._color
        fonts = tuple([truetype(name, size)
                       for name in fonts
                       for size in font_sizes or (65, 70, 75)])
        draw = Draw(image)
        char_images = []
        for c in self._text:
            font = random.choice(fonts)
            c_width, c_height = draw.textsize(c, font=font)
            char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
            char_draw = Draw(char_image)
            char_draw.text((0, 0), c, font=font, fill=color)
            char_image = char_image.crop(char_image.getbbox())
            for drawing in drawings:
                d = getattr(self, drawing)
                char_image = d(char_image)
            char_images.append(char_image)
        width, height = image.size
        offset = int((width - sum(int(i.size[0] * squeeze_factor)
                                  for i in char_images[:-1]) -
                      char_images[-1].size[0]) / 2)
        for char_image in char_images:
            c_width, c_height = char_image.size
            mask = char_image.convert('L').point(lambda i: i * 1.97)
            image.paste(char_image,
                        (offset, int((height - c_height) / 2)),
                        mask)
            offset += int(c_width * squeeze_factor)
        return image

    # draw text
    @staticmethod
    def warp(image, dx_factor=0.27, dy_factor=0.21):
        width, height = image.size
        dx = width * dx_factor
        dy = height * dy_factor
        x1 = int(random.uniform(-dx, dx))
        y1 = int(random.uniform(-dy, dy))
        x2 = int(random.uniform(-dx, dx))
        y2 = int(random.uniform(-dy, dy))
        image2 = Image.new('RGB',
                           (width + abs(x1) + abs(x2),
                            height + abs(y1) + abs(y2)))
        image2.paste(image, (abs(x1), abs(y1)))
        width2, height2 = image2.size
        return image2.transform(
            (width, height), Image.QUAD,
            (x1, y1,
             -x1, height2 - y2,
             width2 + x2, height2 + y2,
             width2 - x2, -y1))

    @staticmethod
    def offset(image, dx_factor=0.1, dy_factor=0.2):
        width, height = image.size
        dx = int(random.random() * width * dx_factor)
        dy = int(random.random() * height * dy_factor)
        image2 = Image.new('RGB', (width + dx, height + dy))
        image2.paste(image, (dx, dy))
        return image2

    @staticmethod
    def rotate(image, angle=25):
        return image.rotate(
            random.uniform(-angle, angle), Image.BILINEAR, expand=1)

    def captcha(self, path=None, fmt='JPEG'):
        """Create a captcha.

        Args:
            path: save path, default None.
            fmt: image format, PNG / JPEG.
        Returns:
            A tuple, (name, text, StringIO.value).
            For example:
                ('fXZJN4AFxHGoU5mIlcsdOypa', 'JGW9', '\x89PNG\r\n\x1a\n\x00\x00\x00\r...')

        """
        image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
        image = self.background(image)
        image = self.text(image, self.fonts, drawings=['warp', 'rotate', 'offset'])
        image = self.curve(image)
        image = self.noise(image)
        image = self.smooth(image)
        name = "".join(random.sample(string.ascii_lowercase + string.ascii_uppercase + '3456789', 24))
        text = "".join(self._text)
        out = BytesIO()
        image.save(out, format=fmt)
        if path:
            image.save(os.path.join(path, name), fmt)
        return name, text, out.getvalue()

    def generate_captcha(self):
        self.initialize()
        return self.captcha("")

captcha = Captcha.instance()

if __name__ == '__main__':
    print(captcha.generate_captcha())
'''
('oNFHu6a9MyWvRtDQmTwhCXJY',
'M5JK', ====>图片中的字符
图片的二进制数据==》b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c $.\' ",#\x1c\x1c(7),01444\x1f\'9=82<.342\xff\xdb\x00C\x01\t\t\t\x0c\x0b\x0c\x18\r\r\x182!\x1c!22222222222222222222222222222222222222222222222222\xff\xc0\x00\x11\x08\x00K\x00\xc8\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00\x01\x02\x03\x11\x04\x05!1\x06\x12AQ\x07aq\x13"2\x81\x08\x14B\x91\xa1\xb1\xc1\t#3R\xf0\x15br\xd1\n\x16$4\xe1%\xf1\x17\x18\x19\x1a&\'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xf7\xba(\xa2\xa8\x02\x8a(\xa0\x02\x91Y[\xee\xb0?CU%\x9e\xeb\xed\xe9\x02\xda\xee\xb6e\xf9\xa6\xdd\xf7O\xa6)\xf6V\x16\xfa|M\x1d\xba\x95Vm\xc4\x12O4\x89\xbd\xde\x85\x9a(\xaar\xd9\x06\xbf\x8e\xf1\xaee@\x83\x1e^\xec!\xfa\x8a\x06\xd9r\x8a\x01\x04d\x1c\x8a)\x8c(\xaa(\xb3G\xa8I4\x97\xcam\x88\xc2\xc3\x81\xf2\x9f\\\xd5\xc0\xea\xdd\x18\x1f\xa1\xa4$\xee:\x8a23\x8c\xd1LaEW\xbc\xba\xfb\x1d\xabO\xe5I.\xdf\xe0\x8cd\x9a\x82\xd2[\xf9\xae\x8c\xb2$if\xc8\n)\x1f\xbc\x07\xde\x95\xc9rW\xb1~\x8a(\xa6PQA \x0c\x93\x8a\xc9\xfe\xd2\x96\xf6\xf0E\xa6\xc9\x13,/\xb6\xe3x9\x1fJM\xd8NI\x1a\xd4QE1\x85\x14Q@\x05\x14Q@\x05\x14Q@\x05P\xd5g\xb9\xb7\xb6\x0fj\xf0+\x03\x96\xf3\x8e\x06*\xfdp\x972\xcbs\xe2\x13-\xebo\x82)\xbc\xaf(\xf4Pz\x1aL\x89\xbb+\x1bv\x9e/\xd3.F\x1d\xde\'\x07\x042\xfe\xb4\xe3\xe2\xcb\x01|mUes\xd02\xaeA\xad/\xec\xfb\x15\x8d\xb1o\x12\xab.\t\xda:W+\xa7hwz_\x8a\x17j\x17\xb3l\x95n\xa0f\xa7TKsV:8\xb5\xdd:\xe1\x18}\xa0F\xd8\xe4?\x04Q\x06\x95n\xfas\xdb\xc9<\xb70\xcawnw\xc9\xfc\r>\xed4\xc6YM\xc0\xb7\xca\x0f\x9c\xb0\x1f.}k/\xc3\xf3\xc9%\xb5\xe6\x9c\x93a\xe1?\xba\x93\xae\x14\xf44\xfdF\xed{3\xa1\x86$\x82\x14\x8a1\x84A\x85\x19\xedY\x1a\xfd\xe3D\xf6\x96i\x9f\xf4\x99\x02\xb1\x07\x04\x0e\xf5a\x9bQ\xb1\xb0\x89U>\xddq\xbb\x0e\xd9\t\xc7\xadTt[\xef\x13\xc7\xb8\x8cZG\xb8\xaf\xa3\x1a\x06\xde\x96D\x16zF\x8dwsp\x88\xd3H\xf16\x1c30\x00\xd6\xb0\xd2\xad\x11@@\xc9\x81\x81\xb5\x8dp\x1a\xa6\xadp<Mq\x12\xcb9\x886\x02Bpjk\xab\x88\xe3x\x8d\xc5\xde\xa1n_\xa6\xe6\xc8\x15*H\xca5c\xaf\x91\xab\x1c\x8dc\xe3X\xac\xe1\xb9\x95\xe3u\xfd\xe0v\xcf5\xd9W\x9ah\xb7\x96\x16z\xfc\xd7\xb7\xd7\x85\xc2\x8cF\xe7\x92\xdfZ\xde\x9b\xc7\x10\xc8\xc6=:\xca{\x993\x81\x81\x80}\xe8SKqB\xb4b\x9f3\xeau\xa7\x80Mrq\xf8\xd1\x7f\xb6\xfe\xc1-\xaf\x967\xec\xdcZ\xb3\xcf\x89\xbcO\x93\x8d+\x8f\xfa\xe6k\x9d\xd7-\xf55\xb9MF\xf2\xd4[\xb4\xa7 \xaf\xaf\xf4\xa5*\x9d\x88\xa9\x89\xd2\xf0\xbf\xdcz\x9d\xd7\xdb\x8c\xd0}\x93\xc9\xf2\xb3\xfb\xdd\xfdq\xedR^O\xf6{Yd\x0c\xa1\x95I\x195\xc4iP\xeb\xfe!\xb3\x8eC\xaa\x08\xed\xc1\xc1\xd9\xc3\x0cU\xab\x8f\x04\xde\\\xaf\xef\xf5\x99d\xdb\xd3p4\xf9\x9b\xd5#EVm^1e\x8d:\x19u\xdd7\xed\x17\x9a\x94\x89\xe6\x12\x02F\xc1p3X\xbaN\xa64?\x12\xcbdnwY\xe4\xa9g==\xf3Rx\x7f\xc2\xd6\xf7\xf1\\\xac\xf7\x17\n\xf0\xcaS\xe4l\n\xc7\xd6\xf4\x03g\xae\xad\x8d\xaa\xc9 p\n\x96\xe4\x9a\x86\xe5d\xecc9TIN\xdf\x89\xd4\xdfx\xd1^\xf5 \xd2\xca\xcb\xce>`~s\xe8+oL\xd7\xad\xf5\t\x9a\xd8\xabCt\x9fz\'\xebY6\xb6V\xda$,.l"H\xa1M\xcbt\xc4\x12\xcf\xf4\xacO\x0b\xda\xdfj\xba\xec\xba\x98\x9c\xa2\xa3\xfc\xccFw\x0fJ\xbef\x9e\xa6\x8a\xa4\xd4\x92z\xb6z=\x14QZ\x1dAE\x14P\x01E\x14P\x01\\w\x8a!\xfb>\xa0\x19cb\xb7Q\xec\xf9G\xf1\x8e\x84\xd7cY\x9a\xed\xb3O\xa73\xc6\xa0\xcb\x11\x12\'\x1e\x94\x9a\xba"j\xe8\xa6\x936\xad\xe17;\x88\x9b\xcb \xed\xea\x18U\x7f\tk\xad\xa8[\xfd\x92\xe3\xfd|#\x00\x9f\xe2\x14x~\xe9M\xfc\xf0\x84\x0b\x15\xc2\xf9\xca=\xfa\x1a\xe5\xf58o4\xfdB\xec\xdb\xca\x91y2\x99\x17\'\r\xcf\xa7\xaf\xd2\xa1\xbbjg)8\xdaF\x82\xdf\xec\xf1\xbd\xcd\xad\xd0\x0f\x05\xc3\x05da\xc1\xf4\xad\x8b\xa4\x1aG\x89\xedgE\tmp\xbeS\x01\xc0\xcfj\xf3\xd8\xef\xae&\xbe7L\xad5\xcew\x06\xee1[:\xcd\xce\xbf\xaa\xdbDn\xa0)l\x14H\n\x0e>\xa4\xd4)\xe8s\xc6\xba\xb3i]\xdfC\xbc\xbc\xf1\x0e\x95d\xa4\xcby\x1eA\xc1U99\xae2\xcb\xc4\xd3\xa5\xfd\xfc\xf6vr\\<\xe7*O\xf0\x81\xd2\xb6\xf4\xaf\ti\xc7K\x8ep<\xcb\x99c\xdc$~@$zW<\xfaU\xd5\x86\xa5-\x9bI3\xb99D\x84m\x0f\x9f\xe5T\xf9\x8d&\xeb;7\xa7\xa0\x96>\x1b\xd5\xf5;\xd6\xbciR\xdd\xdd\xb7\x16\xdd\xcf>\x82\xafj~\x15]1#\xbe\xb8\x99\xef\x11[\xf7\xca\xd9\xe7=\xc5T[;\xed&\xf6\xdeS\x14\xf6\xe5\xe4\x01\x07\x99\xbb>\xc6\xba\x7f\x16\xea\xf1Yh\x8d\x16\xe0e\xb8]\xaa:\xfdi(\xc5+\xb2cJ\x9a\x8br\xfcI\xb4\xef\x0e\xe8f$\xba\x82\xcdYd\\\x8d\xfc\xf1\xf8\xd6\xbd\xbd\x95\xb5\xaa\xed\x82\x08\xe3\x19\xcf\xca\xb8\xaa\x1e\x1aYW\xc3\xf6\x82S\x96\xd9\xc7\xd3\xb5/\xf6\xc3.\xbf\xfd\x99-\xb9P\xc9\xba93\x9d\xdf\xe1Z+$tG\x92);Z\xe6\xada\xf8\xb2\xc7\xed\xda\x0c\xc0\x0c\xb4cx\xfc+r\x99,bX\x9e6\x19\x0c\x084\xda\xba\xb1\xa4\x972i\x9e}\xe0-Ym\xeed\xd3\xe58\x12\x1c\xa7\xd6\xbd\x10\xf21^;u\x14\xba\x17\x88\xceF\xd3\x14\xbb\x87\xd35\xebvwQ\xde\xdaGq\x13\x06G\\\x82+:oK>\x876\x16O\x95\xc1\xee\x8cO\x0c\xc7\xe4\xdd\xeaq\x9c\xe4NN\x0f\xa5n\xb5\xbc-:\xcc\xd1\xa9\x91F\x03\x11\xc8\x15\xcdi\xba\x83\x1f\x18\xde\xdbyl\x88\xeb\x9f\x98`\x92;\x8a\x9b\xc6wwVZTs\xda\xc8\xc8VA\xb8\xad]\xd2W5SJ\r\xf6(\xf8\xf6\x1b\xa9\xad\xa0\xf2\xf3\xf6p\xdf9\xcf\x19\xf7\xad\x1d\x03H\x9a\xc2\xda\xd4\xc3x>\xcf\xb3/\x18\\\xef\'\xbejM\x1e\xfa\x1f\x11h[n\x02\x96e\xdb"\xe7\xf5\xac}\x0fQ}7\xc4\x0f\xa3\x19|\xdbbH\x8c\xf5+\xedS\xa5\xef\xdc\x8bG\x99O\xb9\xda\xd1E\x15\xa1\xd0\x14QE\x00\x14QE\x00\x14\x8d\xf7\x0f\x19\xe3\xa5-\x14\x01\xe53\xff\x00k\xdej\x93Eg\x0c\xb1\xb4\x0e\xcc\x15O+\xcf<\xd2\xe8\xfaM\xce\xa5\xaf,Z\xa4w\x0c\x0f.[<\xe3\xd4\xd7\xa4Eip\x9a\x94\x97\x06\xe1L\x0c0\xb1\x04\x03\x07\xd75%\xe4\x12\xcdl\xebm(\x82c\xd2M\xb9\xc5e\xec\xfa\xb3\x93\xea\xd7|\xd2m\x9c\xf7\x884\xcbm7LK\x8b+E\x06#\x86\x089+\x8csY:\x0f\x89\xed_Lm.\xfc\xec\xdc\n+\x1e\x98>\xb5\xd8\xd8^Cv\xb2[y\xa6i`\xf9e%q\x93X\xba\xa7\x81\xec/\x0bIlM\xbc\xa7\x9e>\xef\xe5M\xa7\xbcK\x94e\xa4\xa9\xfd\xc4Z[\xfd\xaf\xc3\x92\xab\xdc\xcb\x18\xb3s\x86\x89\xb0H\x1d\x07\xd2\xa2\x8bQ\xd6\xf5\x1b\x94\x16\xc2\xd23\xb7p\xe7,\x17\xde\x88|?w\xa4xkP\x85\xa5Wg\x1b\x86\xd3\xd8Uki\xb5\x1bkd\x9c\xbd\x8d\xb0(\x17\xce\x1c\xb9\x14j%}/\xa1\xad-\xa4\x16\x08\xf3\xebw\xc6\xe5\x9cmQ\x8cc=\x80\x15\xcc\xddiw:\xae\xb3og\x0cm\x1d\xa2\x8d\xc8\xa4\xe7b\xfb\xfb\xd4\x96\'R\xd4\xb5Y\x80\xcd\xcb\xe7j\\\x95\xf9\x10z\xe3\xd6\xbb\x9d/M]:\xdfc9\x96S\xcb\xc8z\xb1\xa2\xca@\xa2\xaa\xad\xb4-[@\xb6\xd6\xd1\xc2\x9fu\x14(\xa96\xa9m\xdbF}qX\xf7\xba\xa5\xf5\xbd\xe9\xb6\x8fOvW\x18\x8aa\xca\xe7\xfd\xa1\xd8V\x8d\x9f\xda\xbe\xca\xbfl\xf2\xfc\xff\x00\xe2\xf2\xfaU\xa7\xd0\xddI7dO\x90\x0e\t\x14\xb5\xe5\xfe*\xbe\xd4\xed5\xe7\rq"\xa09\x8fi\xc0\xc5w\x9a&\xab\x0e\xa1\xa5\xc3/\x9c\x86M\xa08\xcfCR\xa4\x9bh\x88VR\x93\x8fc\x98\xf8\x85`6Az\xab\xcev1\xa4\xf0%\xfd\xe4\xa0\xd9\xab\xc5\xf6x\xceX7\xdf\xfc+C\xc6\xf7\xb6\x8d\xa2\xb4\x02h\xda]\xc3\n\x0eMs\x9e\n\x91\xed.no\x042\xca\x88\x98+\x18\xc995\x0fJ\x874\xed\x1cB\xb7]\xcd\xcdf\xef\xfb\x17\xc5\x89\x7f,,\xf0\xbc[r\xa3\xbdU\xd7\xb5\xdf\xed\x9d*X#\x89S\x07$3|\xdf\x95vf(5\x1bX\xda{pU\x80m\x92\x0eG\xd6\xaaK\xe1\xfb\x06\xb7x\xe1\x88@\xcf\xff\x00-#\xe1\x87\xe3V\xd3:%\t4\xec\xf4g%\xa5h\x1a\x8c\xfa\x0cR\xd9J\xd6\xd3\x92C\x068\xdc\xb5\xbf\xa0xdi\xc5nn\x98Ir:\x11\xd1kR{+\x9f\xb1C\x05\xa5\xd9\x85\xa3#22\xee,=\xea\xf2\x82\x14\x02rq\xd6\x85\x1b\x0e\x14\xd2+Z\xea\x16\xf7\x93M\x14,K\xc2p\xe0\xa9\x185j\x90*\xa9$(\x04\xf5\xc0\xa5\xaa4W\xea\x14QE1\x85\x14Q@\x05\x14Q@\x05\x14Q@\x08\x15T\x92\x14\x02z\xe0R\xd1E\x006H\xd6X\x9a7\x19V\x18"\xb1\xe2\xf0\xae\x95\x1bd\xc0\\\xe7#s\x13\x8a\xda\xa2\x95\x84\xd2{\x8c\x8e(\xe1@\x91\xa2\xaa\x8e\x00\x03\x15^;\x1f/Q\x92\xef\xed\x12\xb6\xf1\x8f(\xb7\xc8>\x82\xad\xd1@4\x98QE\x14\xc6`x\xa7A\xfe\xd9\xb2\x06%\x1fi\x8f\xee\x13\xdf\xda\xbc\xf1\xb4Mj\xd2C\x1a\xdbN\x0f\xfb\x19\xc1\xafb\xa2\xa2T\xd4\x9d\xcc*a\xe3Q\xf3l\xcf.\xb0\xf0V\xab|\xea\xf7?\xb9\x8c\xe0\x92\xe7$\x8a\xf4-\'I\xb7\xd2-\x05\xbd\xb8\xe3\xa9c\xd4\x9a\xbfE\x11\x82\x8e\xc3\xa5B\x14\xf6\xdc(\xa2\x8a\xb3`\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x03\xff\xd9')
'''